package org.putrpctorest.parameter.impl;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.putrpctorest.PutRpcToRestError;
import org.putrpctorest.resource.CollectionType;
import org.putrpctorest.resource.Parameter;

public class Definition {

	private String name;
	private Class<?> type;
	private String expression;
	private Definition root;
	private CollectionType collectionType;

	public Definition(Parameter parameter) {
		this(parameter.getName(), parameter.getJavaType(), parameter
				.getCollectionType(), "restParams['" + parameter.getName()
				+ "']", null);
	}

	public Definition(PropertyDescriptor propertyDescriptor, Definition root) {
		this(
				propertyDescriptor.getName(),
				CollectionType
						.getTypeFromPropertyDescriptor(propertyDescriptor),
				CollectionType
						.getCollectionTypeFromPropertyDescriptor(propertyDescriptor),
				root.getExpression() + "." + propertyDescriptor.getName(), root);
	}

	public Definition(String name, Class<?> type,
			CollectionType collectionType, String expression, Definition root) {
		this.name = name;
		this.collectionType = collectionType;
		this.type = type;
		this.expression = expression;
		this.root = root;
	}

	public String getName() {
		return name;
	}

	public Class<?> type() {
		return type;
	}

	public CollectionType getCollectionType() {
		return collectionType;
	}

	public Class<?> getExpressionType() {
		if (collectionType == null) {
			return type;
		} else {
			return collectionType.getActualCollectionType();
		}
	}

	public String getExpression() {
		return expression;
	}

	public Definition getRoot() {
		return root;
	}

	public Correspondences getCorrespondences(
			Definition otherParameterDefinition) {
		List<CORRESPONDENCE> correspondences = new ArrayList<CORRESPONDENCE>();
		if (equals(getName(), otherParameterDefinition.getName())) {
			correspondences.add(CORRESPONDENCE.NAME);
		}
		if (equals(type(), otherParameterDefinition.type())) {
			correspondences.add(CORRESPONDENCE.TYPE);
		}
		if (correspondences.size() == 0) {
			correspondences.add(CORRESPONDENCE.NONE);
		}
		return new Correspondences(correspondences);
	}

	public Expression to(Definition definition) {
		return new Expression(definition.getExpressionType(),
				definition.getExpression(), getExpression());
	}

	public Expression instantiate() {
		return new Expression(getExpressionType(), getExpression(), "new "
				+ getExpressionType().getName() + "()");
	}

	private boolean equals(Object o1, Object o2) {
		if (o1 == null) {
			if (o2 == null) {
				return true;
			} else {
				return false;
			}
		}
		return o1.equals(o2);
	}

	public List<PropertyDescriptor> getReadAndWriteBeanProperties() {
		List<PropertyDescriptor> propertyDescriptors = new ArrayList<PropertyDescriptor>();

		BeanInfo beanInfo;
		try {
			beanInfo = Introspector.getBeanInfo(type);
			for (PropertyDescriptor propertyDescriptor : beanInfo
					.getPropertyDescriptors()) {
				if (propertyDescriptor.getReadMethod() != null
						&& propertyDescriptor.getWriteMethod() != null) {
					propertyDescriptors.add(propertyDescriptor);
				}
			}
			return propertyDescriptors;
		} catch (IntrospectionException e) {
			throw new PutRpcToRestError("could not introspect class "
					+ type.getName() + ". " + e, e);
		}
	}

	Expressions getRootExpressions() {
		Expressions rootExpressions = new Expressions("root");
		Definition root = getRoot();
		while (root != null) {
			rootExpressions.add(root.instantiate());
			root = root.getRoot();
		}
		Collections.reverse(rootExpressions);
		return rootExpressions;
	}

	@Override
	public String toString() {
		StringBuffer tmp = new StringBuffer();
		tmp.append("[ name=");
		tmp.append(name);
		tmp.append(", type=");
		if (collectionType != null) {
			tmp.append(collectionType);
			tmp.append("<");
			tmp.append(type.getName());
			tmp.append(">");
		} else {
			tmp.append(type.getName());
		}
		tmp.append(", expression=");
		tmp.append(expression);
		tmp.append(", root-name=");
		tmp.append(root == null ? "null" : root.getName());
		return tmp.toString();
	}

}
